/* Copyright 2008 Bas van den Berg
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef CMDVISITOR_H
#define CMDVISITOR_H

#include "MudDoor.h"
#include "MudContainer.h"
#include "MudObject.h"

namespace Chronos {

class MudCreature;
class MudExit;
class MudItem;
class MudConsumable;
class MudCredits;
class MudDrink;
class MudFood;
class MudKey;
class MudMobile;
class MudOpenable;
class MudPlayer;
class MudWearable;
class ElevatorButton;

class CmdVisitor {
public:
    virtual ~CmdVisitor() {}
    virtual void visit(MudObject&) {}
    virtual void visit(MudOpenable&) {}
    virtual void visit(MudLockable&) {}
    virtual void visit(MudContainer& o) { visit((MudObject&)o); }
    virtual void visit(MudOpenableContainer& o) { visit((MudContainer&)o); }
    virtual void visit(MudLockedContainer& o) { visit((MudOpenableContainer&)o); }
    virtual void visit(MudItem& o) { visit((MudObject&)o); }
    virtual void visit(MudCredits& o) { visit((MudItem&)o); }
    virtual void visit(MudConsumable& o) { visit((MudItem&)o); }
    virtual void visit(MudDrink& o) { visit((MudConsumable&)o); }
    virtual void visit(MudFood& o) { visit((MudConsumable&)o); }
    virtual void visit(MudWearable& o) { visit((MudItem&)o); }
    virtual void visit(MudExit& o) { visit((MudObject&)o); }
    virtual void visit(MudDoor& o) { visit((MudExit&)o); }
    virtual void visit(MudLockedDoor& o) { visit((MudDoor&)o); }
    virtual void visit(MudMobile& o) { visit((MudObject&)o); }
    virtual void visit(MudKey& o) { visit((MudItem&)o); }
    virtual void visit(MudPlayer& o) { visit((MudMobile&)o); }
    virtual void visit(MudCreature& o) { visit((MudMobile&)o); }
    virtual void visit(ElevatorButton& o) { visit((MudObject&)o); }
};

#define VISITOR_ACCEPT(a) void a::accept(CmdVisitor& v) { v.visit(*this); }

template <class T> class TypeVisitor : public CmdVisitor {
public:
    virtual void visit(T& obj) {
        object = &obj;
    }
    static T* getType(MudObject& obj) {
        TypeVisitor<T> visitor(obj);
        return visitor.object;
    }
private:
    TypeVisitor(MudObject& o) : object(0) {
        o.accept(*this);
    }
    T* object;
};


template<> class TypeVisitor<MudOpenable> : public CmdVisitor {
public:
    virtual void visit(MudDoor& obj) {
        openable = obj.toOpenable();   // NOTE: get 2nd base class pointer
    }
    virtual void visit(MudOpenableContainer& obj) {
        openable = obj.toOpenable();   // NOTE: get 2nd base class pointer
    }
    static MudOpenable* getType(MudObject& obj) {
        TypeVisitor<MudOpenable> visitor(obj);
        return visitor.openable;
    }
private:
    TypeVisitor(MudObject& o) : openable(0) {
        o.accept(*this);
    }
    MudOpenable* openable;
};


template<> class TypeVisitor<MudLockable> : public CmdVisitor {
public:
    virtual void visit(MudLockedDoor& obj) {
        lockable = obj.toLockable();   // NOTE: get 2nd base class pointer
    }
    virtual void visit(MudLockedContainer& obj) {
        lockable = obj.toLockable();   // NOTE: get 2nd base class pointer
    }
    static MudLockable* getType(MudObject& obj) {
        TypeVisitor<MudLockable> visitor(obj);
        return visitor.lockable;
    }
private:
    TypeVisitor(MudObject& o) : lockable(0) {
        o.accept(*this);
    }
    MudLockable* lockable;
};

}

#endif

